package net.sea.setup.action;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.dom.DOMElement;

import net.sea.commons.constants.AppConstants;
import net.sea.commons.context.AppContext;
import net.sea.commons.util.StringUtils;
import net.sea.commons.util.XMLUtils;
import net.sea.setup.action.parse.Action;
import net.sea.setup.action.parse.Deploy;
import net.sea.setup.action.parse.Scanner;

/**
 * 安装（卸载）程序入口类
 * 
 * @author 华成伍
 * 
 */
public class Setup {
	/**
	 * 日志对象
	 */
	private Logger logger = Logger.getLogger(this.getClass());
	/**
	 * 安装类型
	 */
	public final static int INSTALL = 1;
	/**
	 * 卸载类型
	 */
	public final static int UNINSTALL = 0;
	// 系统上下文
	private AppContext context;
	/**
	 * 操作映射列表
	 */
	private Map<String, String> actionMap = new HashMap<String, String>();

	/**
	 * 默认构造函数
	 */
	public Setup() {
		// 初始化系统内置的操作标签
		actionMap.put("unzip", "com.ec.setup.action.impl.UnzipAction");
		actionMap.put("copy", "com.ec.setup.action.impl.CopyfileAction");
	}

	/**
	 * 检查安装参数是否齐全
	 */
	private void check() {
		if (context == null) {
			throw new RuntimeException("系统上下文对象为空");
		}
		// 验证必需参数
		String installRoot = (String) context
				.getAttribute(AppConstants.INSTALL_ROOT_KEY);
		if (StringUtils.isEmpty(installRoot)) {
			throw new RuntimeException("未找到安装程序目录参数");
		}
		String installDir = (String) context
				.getAttribute(AppConstants.INSTALL_DIR_KEY);
		if (StringUtils.isEmpty(installDir)) {
			throw new RuntimeException("用户为选择安装目录");
		}
		// 验证数据库初始化所需参数
		boolean flag = false;
		try {
			flag = (Boolean) context.getAttribute(AppConstants.DB_INIT_FLAG);
		} catch (Exception e) {
			throw new RuntimeException("数据库初始化标记错误");
		}
		if (flag) {
			String dbType = (String) context.getAttribute(AppConstants.DB_TYPE);
			if (StringUtils.isEmpty(dbType)
					|| !(AppConstants.DB_DB2.equals(dbType)
							|| AppConstants.DB_MYSQL.equals(dbType)
							|| AppConstants.DB_ORACLE.equals(dbType) || AppConstants.DB_SQLSERVER
								.equals(dbType))) {
				throw new RuntimeException("不支持的数据库类型");
			}
			String dbUrl = (String) context.getAttribute(AppConstants.DB_URL);
			if (StringUtils.isEmpty(dbUrl)) {
				throw new RuntimeException("数据库连接字符串为空");
			}
			String dbUser = (String) context.getAttribute(AppConstants.DB_USER);
			if (StringUtils.isEmpty(dbUser)) {
				throw new RuntimeException("数据库连用户名为空");
			}
			String dbDriverClass = (String) context
					.getAttribute(AppConstants.DB_DRIVER_CLASS);
			if (StringUtils.isEmpty(dbDriverClass)) {
				throw new RuntimeException("数据库驱动类为空");
			}
			String dbDriverJar = (String) context
					.getAttribute(AppConstants.DB_DRIVER_JAR);
			if (StringUtils.isEmpty(dbDriverJar)) {
				throw new RuntimeException("数据库驱动JAR包为空");
			}
		}
	}

	/**
	 * 贷参构造函数
	 * 
	 * @param context
	 *            系统上下文
	 */
	public Setup(AppContext context) {
		this();
		this.context = context;
	}

	/**
	 * 获取上下文对象
	 * 
	 * @return 上下文对象
	 */
	public AppContext getContext() {
		return context;
	}

	/**
	 * 设置上下文对象
	 * 
	 * @param context
	 *            上下文对象
	 */
	public void setContext(AppContext context) {
		this.context = context;
	}

	/**
	 * 安装（卸载）启动入口
	 * 
	 * @param type
	 *            操作类型，支持INSTALL、UNINSTALL类型
	 */
	public void start(int type) {
		logger.info("平台开始安装……");
		// 检查安装参数是否齐全
		check();
		long startTime = System.currentTimeMillis();
		List<Deploy> deploys = parse();
		// TODO 目前只实现单层的操作，不支持嵌套操作
		List<Action> actions = new ArrayList<Action>();
		for (Deploy deploy : deploys) {
			actions.addAll(deploy.getActions());
		}
		try {
			// 初始化操作映射
			initActionMapping();
			// 设置操作的权重
			context.setAttribute(AppConstants.WEIGHT_KEY, actions.size());
			// 系统创建未建立的用户安装目录
			File installDir = new File(
					(String) context.getAttribute(AppConstants.INSTALL_DIR_KEY));
			if (!installDir.exists()) {
				installDir.mkdirs();
			}

			int i = 0;
			// 遍历所有的操作，执行安装或卸载方法
			if (type == INSTALL) {
				for (Action action : actions) {
					// added by li_zhen 20150304 begin 只安装选择的组件
					@SuppressWarnings("unchecked")
					List<String> moduleIds = (List<String>) AppContext
							.getInstance().getAttribute(
									AppConstants.MODULE_LIST);
					// 参数列表
					Map<String, String> parMap = action.getParMap();
					if (parMap.containsKey("comp_id")
							&& (!moduleIds.contains(parMap.get("comp_id")))) {
						continue;
					}
					// added by li_zhen 20150304 end

					// 从系统默认操作列表取对应的操作类
					String className = actionMap.get(action.getActionName());
					IAction ac = (IAction) Class.forName(className)
							.newInstance();
					ac.install(context, parMap);
					i++;
					context.setAttribute(AppConstants.INSTALL_OFFSET_KEY, i
							* 100 / actions.size());
				}
			} else if (type == UNINSTALL) {
				for (Action action : actions) {
					// 从系统默认操作列表取对应的操作类
					String className = actionMap.get(action.getActionName());
					IAction ac = (IAction) Class.forName(className)
							.newInstance();
					ac.uninstall(context, action.getParMap());
					i++;
					context.setAttribute(AppConstants.INSTALL_OFFSET_KEY, i
							* 100 / actions.size());
				}
			}
			// 创建卸载程序
			// createUninstall(actions);
			context.setAttribute(AppConstants.FINISH_FLAG_KEY, true);
			logger.info("平台安装完成……");
		} catch (InstantiationException e) {
			exitOnErr("安装操作类创建失败", e);
		} catch (IllegalAccessException e) {
			exitOnErr("安装操作类创建失败", e);
		} catch (ClassNotFoundException e) {
			exitOnErr("未找到指定的安装操作类", e);
		} catch (FileNotFoundException e) {
			exitOnErr("未找到操作类的映射配置文件", e);
		} catch (IOException e) {
			exitOnErr("读取映射配置错误", e);
		} catch (Exception e) {
			exitOnErr("安装操作异常", e);
		}
		long endTime = System.currentTimeMillis();
		if (logger.isDebugEnabled()) {
			logger.debug("安装总耗时：" + (endTime - startTime) / 100.0 + "秒");
		}
	}

	/**
	 * 系统异常退出
	 * @author 华成伍
	 * @param msg
	 *            异常提示信息
	 * @param e
	 *            抛出的异常
	 */
	private void exitOnErr(String msg, Exception e) {
		logger.error(msg, e);
		System.exit(1);
	}

	/**
	 * 创建卸载程序的资源
	 * 
	 * @param actions
	 *            安装操作列表
	 */
	@SuppressWarnings("unused")
	private void createUninstall(List<Action> actions) {
		String insallDir = (String) context
				.getAttribute(AppConstants.INSTALL_DIR_KEY);
		File uninstallDir = new File(insallDir, "uninstall");
		// 创建卸载程序目录
		if (!uninstallDir.exists()) {
			uninstallDir.mkdirs();
		}
		// 生成卸载配置文件
		if (actions == null) {
			return;
		}
		// 创建DOM根节点
		Document document = DocumentHelper.createDocument();
		Element root = document.addElement("deploy");
		for (int i = actions.size() - 1; i >= 0; i--) {
			root.add(createElement(actions.get(i)));
		}
		// 写入文件
		XMLUtils.wirteXML(document, new File(uninstallDir, "deploy.xml"));
	}

	/**
	 * 创建Dom元素
	 * 
	 * @param action
	 *            部署操作对象
	 * @return Dom元素
	 */
	private Element createElement(Action action) {
		// 创建元素对象
		Element e = new DOMElement(action.getActionName());
		// 构建元素属性
		Map<String, String> parMap = action.getParMap();
		Set<String> keyset = parMap.keySet();
		for (String key : keyset) {
			e.addAttribute(key, parMap.get(key));
		}
		// 创建元素子节点
		List<Action> actions = action.getActions();
		if (actions != null) {
			for (Action child : actions) {
				e.add(createElement(child));
			}
		}
		return e;
	}

	/**
	 * 初始化所有注册的操作映射
	 * 
	 * @throws IOException
	 */
	private void initActionMapping() throws IOException {
		// 读取扩展操作
		Properties properties = new Properties();
		properties.load(this.getClass().getResourceAsStream(
				AppConstants.ACTION_MAPPING_FILE));
		// 把扩展操作加入系统操作列表中
		Enumeration<?> propNames = properties.propertyNames();
		while (propNames.hasMoreElements()) {
			String nextElement = (String) propNames.nextElement();
			actionMap.put(nextElement, properties.getProperty(nextElement));
		}
		if (logger.isDebugEnabled()) {
			logger.debug("所有的Action映射为：" + actionMap);
		}
	}

	/**
	 * 解析安装文件
	 * 
	 * @return 安装文件信息列表
	 */
	private List<Deploy> parse() {
		// 扫描安装配置文件
		Scanner scanner = new Scanner();
		List<File> deployFiles = scanner.scan();
		if (logger.isDebugEnabled()) {
			logger.debug("解析到的安装文件为：" + deployFiles);
		}
		List<Deploy> list = new ArrayList<Deploy>();
		try {
			for (File file : deployFiles) {
				Deploy deploy = new Deploy();
				Document doc = XMLUtils.getDocument(file);
				Element root = doc.getRootElement();
				List<Action> actionList = new ArrayList<Action>();
				// 获取安装文件中的所有action
				List<Element> actionElmemts = XMLUtils.getChildren(root);
				for (Element element : actionElmemts) {
					actionList.add(createAction(element));
				}
				deploy.setActions(actionList);
				list.add(deploy);
			}
		} catch (Exception e) {
			logger.error("安装文件解析异常", e);
		}
		return list;
	}

	/**
	 * 生成操作项对象
	 * 
	 * @param element
	 *            xml的节点
	 * @return 操作项对象
	 */
	private Action createAction(Element element) {
		Action action = new Action();
		action.setActionName(element.getName());
		action.setParMap(XMLUtils.getAttributes(element));
		List<Element> children = XMLUtils.getChildren(element);
		// 递归解析子操作项
		if (children != null && children.size() > 0) {
			List<Action> childActions = new ArrayList<Action>();
			for (Element e : children) {
				childActions.add(createAction(e));
			}
			action.setActions(childActions);
		}
		return action;
	}

	/**
	 * 获取Action的所有映射
	 * 
	 * @return Action的所有映射
	 */
	public Map<String, String> getActions() {
		return actionMap;
	}
}
